/*
    Kelompok    2
    Kelas       2
    Anggota :
    1. Yudha Wastu Prawira      / 13509002
    2. Robertus Theodore        / 13509008
    3. Rendy Bambang Junior     / 13509036
    4. Hartono Sulaiman Wijaya  / 13509046
    5. Daniel Prihartoni        / 13509088
    6. Raydhitya Yosep          / 13509092

*/
#include "GameState.h"

//ctor game
GameState::GameState(){
	active = true;
}

//ctor untuk new game
GameState::GameState(string playerName){
	initialize(playerName);
	active = true;
}

//ctor untuk load game
GameState::GameState(SaveInfo saveInfo){
	initialize(saveInfo);
	active = true;
}

//dtor
GameState::~GameState(){
	delete player;
	delete slashDwarf;
	delete waterDwarf;
	delete harvestDwarf;
}

//inisialisasi new game
void GameState::initialize(string playerName){
	//isi nama pemain
	player = new Player(playerName);
	initialize();
}

//inisialisasi load game
void GameState::initialize(SaveInfo saveInfo){
	//isi nama pemain
	player = new Player(saveInfo.PI.name);
	initialize();
    player->initialize(saveInfo);
    waterDwarf->initialize(saveInfo);
    slashDwarf->initialize(saveInfo);
    harvestDwarf->initialize(saveInfo);
    farm.initialize(saveInfo);
}

//inisialisasi aktivasi game
void GameState::initialize(){
	//inisialisasi default player
	player->initialize();
	//inisialisasi default farm
	farm.initialize();
	//inisialisasi dwarf
	waterDwarf = new Dwarf(WATERING_DWARF_CODE);
	slashDwarf = new Dwarf(SLASHING_DWARF_CODE);
	harvestDwarf = new Dwarf(HARVESTING_DWARF_CODE);
}

//proses input untuk menjadi info tampilan
DisplayInfo GameState::update(CommandToken commandToken){

	//clear info tampilan
	displayInfo.message.clear();
	displayInfo.prompt.clear();

	//state tetap state game secara default
	displayInfo.state = GAME_STATE;

	//kalo player habis energi
	int curTimeLeft = player->getTimeLeft();
	if (curTimeLeft<=0){
		player->sleep();
		farm.nextDay();
		displayInfo.message.push_back("You're too tired.");
		displayInfo.message.push_back("Sleeping...");
		displayInfo.message.push_back("You've slept well.");
		displayInfo.player = *player;
		return displayInfo;
	}

	//proses perintah user yang dapat dilakukan di farm/home/store
	switch (commandToken.command){
	case INVENTORY_COMMAND_CODE :{
		Inventory inventory = player->getMyInventory();
		displayInfo.message.push_back("My Inventory");
		for (int slot = 0; (unsigned) slot < inventory.size(); ++slot){
			displayInfo.message.push_back("Slot " + intToString(slot+1) + " " + Resource::instance()->getItemName(inventory[slot].item) + " " + intToString(inventory[slot].amount));
		}
		for (int i=inventory.size()+1; i<=3; i++){
			displayInfo.message.push_back("Slot " + intToString(i) + " - -");
		}
		//displayInfo.message.push_back("Days "+intToString(player->getDay()));
	break;}
	case PLANT_COMMAND_CODE :{
		Plant * plant;
		plant = (farm.getFarmPlan())[player->getFrontPos().x][player->getFrontPos().y].plant;
		if (plant != NULL){
			displayInfo.message.push_back("Name : " + plant->getName());
			displayInfo.message.push_back("Plant Type : " + intToString(plant->getPlantType()));
			displayInfo.message.push_back("Happy Meter : " + intToString(plant->getHappyMeter()));
			displayInfo.message.push_back("Age Left : " + intToString(plant->getAgeLeft()));
			displayInfo.message.push_back("Harvest Point : " + intToString(plant->getHarvestPoint()));
			displayInfo.message.push_back("Harvest Type : " + intToString(plant->getHarvestType()));
		}
	break;}
	case EAT_COMMAND_CODE :{
	    if (player->eating(atoi(commandToken.stringParam[0].c_str())))
            displayInfo.message.push_back("You've eat that.");
        else
            displayInfo.message.push_back("That's not fruit.");
    break;
	}
	default :{
		//proses perintah user berdasarkan posisi user (home/farm/store)
		switch(player->getCurArea()){
		//proses perintah bila pemain di home
		case HOME_CODE :{
		    p_App->p_Input->mode = COMMAND_MODE;
            displayInfo.mode = COMMAND_MODE;
			switch (commandToken.command){
			//untuk command teleport
			case TELEPORT_COMMAND_CODE :{
                if (!commandToken.stringParam.empty()){
                    if (commandToken.stringParam[0] == HOME_STRING)
                        displayInfo.message.push_back("You're already here");
                    else if (commandToken.stringParam[0] == STORE_STRING)
                        displayInfo.message.push_back("You can't go to store. Go to farm first");
                    else if (commandToken.stringParam[0] == FARM_STRING){
                        displayInfo.message.push_back("Teleported to Farm");
                        player->teleport(FARM_CODE);
                        player->decTimeLeft();
                    }
                    else if (commandToken.stringParam[0] == HELP_STRING){
                        displayInfo.message.push_back("Accessible area(s) :");
                        displayInfo.message.push_back("- Farm");
                    }
                    else
                        displayInfo.message.push_back("Unknown place. Try again.");
                } else {
                    displayInfo.message.push_back("You must insert one argument!");
                }
			break;}

			//untuk command save
			case SAVE_COMMAND_CODE :{
			    SaveInfo saveInfo;
			    player->getSaveInfo(saveInfo);
			    farm.getSaveInfo(saveInfo);
			    waterDwarf->getSaveInfo(saveInfo);
			    slashDwarf->getSaveInfo(saveInfo);
			    harvestDwarf->getSaveInfo(saveInfo);
				Resource::instance()->saveGame(saveInfo);
				displayInfo.message.push_back("Your game is saved!");
			break;}

			//untuk command sleep
			case SLEEP_COMMAND_CODE :{
			//kembalikan stamina pemain
				player->sleep();
				displayInfo.message.push_back("Player has slept and wake up.");
			//ubah atribut kebun karena hari berganti
				farm.nextDay();
			break;}

			//untuk command exit
			case EXIT_COMMAND_CODE :
				active = false;
			break;

			//bila command tidak valid
			default :
				displayInfo.message.push_back("Command doesn't recognized");
			break;
			}
		break;}

		//perintah-perintah toko
		case STORE_CODE :{
		    p_App->p_Input->mode = COMMAND_MODE;
            displayInfo.mode = COMMAND_MODE;
			switch(commandToken.command){
			//untuk command teleport
			case TELEPORT_COMMAND_CODE :{
                if (!commandToken.stringParam.empty()){
                    if (commandToken.stringParam[0] == STORE_STRING)
                        displayInfo.message.push_back("You're already here");
                    else if (commandToken.stringParam[0] == HOME_STRING)
                        displayInfo.message.push_back("You can't go to home. Go to farm first");
                    else if (commandToken.stringParam[0] == FARM_STRING){
                        displayInfo.message.push_back("Teleported to Farm");
                        player->teleport(FARM_CODE);
                        player->decTimeLeft();
                    }else if (commandToken.stringParam[0] == HELP_STRING){
                        displayInfo.message.push_back("Accessible area(s) :");
                        displayInfo.message.push_back("- Farm");
                    }
                    else
                        displayInfo.message.push_back("Unknown place. Try again.");
                } else
                    displayInfo.message.push_back("You must insert one argument");
			break;}

			//untuk command store, menampilkan daftar barang dan harga barang
			case STORE_COMMAND_CODE :{
				//mengambil informasi dari Resource
				storeInfo = Resource::instance()->getStoreInfo();
				//menampilkan
				displayInfo.message.push_back("List of Item and Prices");
				int pos;
				for (pos = 6; (unsigned) pos < storeInfo.size(); ++pos){
					displayInfo.message.push_back(storeInfo[pos].name + " " + intToString(storeInfo[pos].price));
				}
			break;}

			//untuk membeli barang tertentu

			case BUY_COMMAND_CODE :{
				//bila uang pemain tidak cukup, tampilkan pesan
				if (!commandToken.stringParam.empty()){
                    if(!player->buy(Resource::instance()->getItemCode(commandToken.stringParam[0]),atoi(commandToken.stringParam[1].c_str())))
                        displayInfo.message.push_back("Insufficient funds or no empty slot in inventory");
                    else{
                        player->decTimeLeft();
                        displayInfo.message.push_back("You've bought "+commandToken.stringParam[1]+" "+commandToken.stringParam[0]);
                    }
				} else {
                    displayInfo.message.push_back("You must insert two arguments two buy something!");
                    displayInfo.message.push_back("buy <item-name> <amount> #");

				}
			break;}

			//untuk menjual barang di inventory player
			case SELL_COMMAND_CODE :{
			    if (!commandToken.stringParam.empty()){
                    //bila barang pemain tidak cukup, tampilkan pesan
                    if(!player->sell(atoi(commandToken.stringParam[0].c_str()),atoi(commandToken.stringParam[1].c_str())))
                        displayInfo.message.push_back("There's nothing to sell in that slot");
                    else{
                        displayInfo.message.push_back("You've sold item in slot "+commandToken.stringParam[0]);
                        player->decTimeLeft();
                    }
			    } else {
                    displayInfo.message.push_back("You must insert two arguments two sell something!");
			    }
			break;}

			//bila command tidak valid
			default :
				displayInfo.message.push_back("Command doesn't recognized");
			break;
		}
		break;}

		//perintah-perintah di kebun
		case FARM_CODE :
			switch(commandToken.command){
			//untuk command teleport
			case ADVENTURE_COMMAND_CODE :{
                p_App->p_Input->mode = ADVENTURE_MODE;
                displayInfo.mode = ADVENTURE_MODE;
			break;}
			case COMMAND_COMMAND_CODE :{
			    p_App->p_Input->mode = COMMAND_MODE;
                displayInfo.mode = COMMAND_MODE;
			break;}

			case TELEPORT_COMMAND_CODE :{
			     if (!commandToken.stringParam.empty()){
                    if (commandToken.stringParam[0] == HOME_STRING){
                        displayInfo.message.push_back("Teleported to Home");
                        player->teleport(HOME_CODE);
                        player->decTimeLeft();
                    }
                    else if (commandToken.stringParam[0] == STORE_STRING){
                        displayInfo.message.push_back("Teleported to Store");
                        player->teleport(STORE_CODE);
                        player->decTimeLeft();
                    }
                    else if (commandToken.stringParam[0] == FARM_STRING)
                        displayInfo.message.push_back("You're already here");
                    else if (commandToken.stringParam[0] == HELP_STRING){
                        displayInfo.message.push_back("Accessible area(s) :");
                        displayInfo.message.push_back("- Home");
                        displayInfo.message.push_back("- Store");
                    }
                    else
                        displayInfo.message.push_back("Unknown place. Try again.");
			     } else {
                    displayInfo.message.push_back("You must insert one argument");
			     }
			break;}

			//untuk command gerak ke atas
			case UP_COMMAND_CODE :{
				//bila pemain belum menghadap ke arah yang benar, dihadapkan dulu
				int step = 1;
				if (!commandToken.stringParam.empty()){
					step = atoi(commandToken.stringParam[0].c_str());
				}
				if (player->getCurDir() != UP){
					player->changeDir(UP);
					--step;
					player->decTimeLeft();
				}
				//bila pemain habis waktu
				if (player->getTimeLeft() == 0){
					player->sleep();
					farm.nextDay();
					displayInfo.message.push_back("You're too tired.");
					displayInfo.message.push_back("Sleeping...");
					displayInfo.message.push_back("You've slept well.");
					displayInfo.player = *player;
					return displayInfo;
				}
				//bila tidak, lanjut gerak
				bool moveable = !player->isFaceOutside();
				while(moveable && (step>0)){
					moveable = player->move(farm);
					--step;
					player->decTimeLeft();
					if(player->isFaceOutside()){
						moveable = false;
					}
				}
				if (!moveable)
					displayInfo.message.push_back("Cannot move there");
				//cek waktu lagi
				if (player->getTimeLeft() <= 0){
					player->sleep();
					farm.nextDay();
					displayInfo.message.push_back("You're too tired.");
					displayInfo.message.push_back("Sleeping...");
					displayInfo.message.push_back("You've slept well.");
					displayInfo.player = *player;
					return displayInfo;
				}
			break;}

			//untuk command gerak ke kanan
			case RIGHT_COMMAND_CODE :{
				//bila pemain belum menghadap ke arah yang benar, dihadapkan dulu
				//int step = atoi(commandToken.stringParam[0].c_str());
				int step = 1;
				if (!commandToken.stringParam.empty()){
					step = atoi(commandToken.stringParam[0].c_str());
				}
				if (player->getCurDir() != RIGHT){
					player->changeDir(RIGHT);
					--step;
					player->decTimeLeft();
				}
				//bila pemain habis waktu
				if (player->getTimeLeft() == 0){
					player->sleep();
					farm.nextDay();
					displayInfo.message.push_back("You're too tired.");
					displayInfo.message.push_back("Sleeping...");
					displayInfo.message.push_back("You've slept well.");
					displayInfo.player = *player;
					return displayInfo;
				}
				//bila tidak, lanjut gerak
				bool moveable = !player->isFaceOutside();
				while(moveable && (step>0)){
					moveable = player->move(farm);
					--step;
					player->decTimeLeft();
					if(player->isFaceOutside()){
						moveable = false;
					}
				}
				if (!moveable)
					displayInfo.message.push_back("Cannot move there");
				//cek waktu lagi
				if (player->getTimeLeft() <= 0){
					player->sleep();
					farm.nextDay();
					displayInfo.message.push_back("You're too tired.");
					displayInfo.message.push_back("Sleeping...");
					displayInfo.message.push_back("You've slept well.");
					displayInfo.player = *player;
					return displayInfo;
				}
			break;}

			//untuk command gerak ke bawah
			case DOWN_COMMAND_CODE :{
				//bila pemain belum menghadap ke arah yang benar, dihadapkan dulu
				//int step = atoi(commandToken.stringParam[0].c_str());
				int step = 1;
				if (!commandToken.stringParam.empty()){
					step = atoi(commandToken.stringParam[0].c_str());
				}
				if (player->getCurDir() != DOWN){
					player->changeDir(DOWN);
					--step;
					player->decTimeLeft();
				}
				//bila pemain habis waktu
				if (player->getTimeLeft() == 0){
					player->sleep();
					farm.nextDay();
					displayInfo.message.push_back("You're too tired.");
					displayInfo.message.push_back("Sleeping...");
					displayInfo.message.push_back("You've slept well.");
					displayInfo.player = *player;
					return displayInfo;
				}
				//bila tidak, lanjut gerak
				bool moveable = !player->isFaceOutside();
				while(moveable && (step>0)){
					moveable = player->move(farm);
					--step;
					player->decTimeLeft();
					if(player->isFaceOutside()){
						moveable = false;
					}
				}
				if (!moveable)
					displayInfo.message.push_back("Cannot move there");
				//cek waktu lagi
				if (player->getTimeLeft() <= 0){
					player->sleep();
					farm.nextDay();
					displayInfo.message.push_back("You're too tired.");
					displayInfo.message.push_back("Sleeping...");
					displayInfo.message.push_back("You've slept well.");
					displayInfo.player = *player;
					return displayInfo;
				}

			break;}

			//untuk command gerak ke bawah
			case LEFT_COMMAND_CODE :{
				//bila pemain belum menghadap ke arah yang benar, dihadapkan dulu
				//int step = atoi(commandToken.stringParam[0].c_str());
				int step = 1;
				if (!commandToken.stringParam.empty()){
					step = atoi(commandToken.stringParam[0].c_str());
				}
				if (player->getCurDir() != LEFT){
					player->changeDir(LEFT);
					--step;
					player->decTimeLeft();
				}
				//bila pemain habis waktu
				if (player->getTimeLeft() == 0){
					player->sleep();
					farm.nextDay();
					displayInfo.message.push_back("You're too tired.");
					displayInfo.message.push_back("Sleeping...");
					displayInfo.message.push_back("You've slept well.");
					displayInfo.player = *player;
					return displayInfo;
				}
				//bila tidak, lanjut gerak
				bool moveable = !player->isFaceOutside();
				while(moveable && (step>0)){
					moveable = player->move(farm);
					--step;
					player->decTimeLeft();
					if(player->isFaceOutside()){
						moveable = false;
					}
				}
				if (!moveable)
					displayInfo.message.push_back("Cannot move there");
				//cek waktu lagi
				if (player->getTimeLeft() <= 0){
					player->sleep();
					farm.nextDay();
					displayInfo.message.push_back("You're too tired.");
					displayInfo.message.push_back("Sleeping...");
					displayInfo.message.push_back("You've slept well.");
					displayInfo.player=*player;
					return displayInfo;
				}
			break;}

			//untuk command plow
			case PLOW_COMMAND_CODE :
				try{
					player->plow(farm);
					player->decTimeLeft();
				}
				catch(char * errMsg){
					displayInfo.message.push_back((string) errMsg);
				}
			break;

			//untuk command put
			case PUT_COMMAND_CODE :
				//bila gagal, munculkan pesan
				//jika didepannya selling Point, maka jual, jika tidak, tanam
				if (player->isFaceOutside())
					displayInfo.message.push_back("Cannot sell or plant there!");
				else{
					if ((player->getFrontPos().x == DEFAULT_SELLING_POINT_X) && (player->getFrontPos().y == DEFAULT_SELLING_POINT_Y))
						if (!player->sell(atoi(commandToken.stringParam[0].c_str()),atoi(commandToken.stringParam[1].c_str())))
							displayInfo.message.push_back("Either item's amount is insufficient or the slot contains no fruit!");
						else{
							player->decTimeLeft();
							displayInfo.message.push_back("You've sold item in slot "+commandToken.stringParam[0]);
						}
					else{
						if (player->getItemType(atoi(commandToken.stringParam[0].c_str())) == SEED){
							try {
								player->seed(farm,atoi(commandToken.stringParam[0].c_str()));
								player->decTimeLeft();
							}
							catch(char * errMsg) {
								displayInfo.message.push_back((string) errMsg);
							}
						}
						else
							displayInfo.message.push_back("Cannot sell crop here. Go to selling point.");
					}
				}
			break;

			//untuk command slash
			case SLASH_COMMAND_CODE :
				//bila gagal, munculkan pesan
				if (player->isFaceOutside())
					displayInfo.message.push_back("Cannot slash there!");
				else{
					if (player->slash(farm))
						player->decTimeLeft();
					else
						displayInfo.message.push_back("There's nothing to slash!");
				}
			break;

			//untuk command water
			case WATER_COMMAND_CODE :
				//bila gagal, munculkan pesan
				if (player->isFaceOutside())
					displayInfo.message.push_back("Cannot water there!");
				else{
					if (player->water(farm))
						player->decTimeLeft();
					else
						displayInfo.message.push_back("Cannot water there!");
				}
			break;

			//untuk command harvest
			case HARVEST_COMMAND_CODE :
				//bila gagal, munculkan pesan
				if (player->isFaceOutside())
					displayInfo.message.push_back("Cannot harvest there!");
				else{
					if (!player->harvest(farm))
						displayInfo.message.push_back("Either nothing to harvest or your inventory is full!");
					else{
						player->decTimeLeft();
						displayInfo.message.push_back("Fruit is harvested");
					}
				}
			break;

			//untuk set status kurcaci jadi tidur
			case SLEEP_COMMAND_CODE :
				//tidurkan kurcaci berdasarkan kodenya, tampilkan pesan bila kurcaci tersebut telah tidur
				switch(atoi(commandToken.stringParam[0].c_str())){
				case WATERING_DWARF_CODE :
					if (waterDwarf->getStatus() == SLEEP)
						displayInfo.message.push_back("He's already slept");
					else{
						waterDwarf->setStatus(SLEEP);
						player->decTimeLeft();
					}
				break;
				case HARVESTING_DWARF_CODE :
					if (harvestDwarf->getStatus() == SLEEP)
						displayInfo.message.push_back("He's already slept");
					else{
						harvestDwarf->setStatus(SLEEP);
						player->decTimeLeft();
					}
				break;
				case SLASHING_DWARF_CODE :
					if (slashDwarf->getStatus() == SLEEP)
						displayInfo.message.push_back("He's already slept");
					else{
						slashDwarf->setStatus(SLEEP);
						player->decTimeLeft();
					}
				break;
				default :
					displayInfo.message.push_back("Unknown dwarf type");
				break;
				}
			break;

			//untuk membangunkan kurcaci
			case WAKEUP_COMMAND_CODE :
				//bangunkan kurcaci berdasarkan kodenya, tampilkan pesan bila kurcaci tersebut telah bangun
				switch(atoi(commandToken.stringParam[0].c_str())){
				case WATERING_DWARF_CODE :
					if (waterDwarf->getStatus() == WAKE_UP)
						displayInfo.message.push_back("He's awake");
					else{
						waterDwarf->setStatus(WAKE_UP);
						player->decTimeLeft();
					}
				break;
				case HARVESTING_DWARF_CODE :
					if (harvestDwarf->getStatus() == WAKE_UP)
						displayInfo.message.push_back("He's awake");
					else{
						harvestDwarf->setStatus(WAKE_UP);
						player->decTimeLeft();
					}
				break;
				case SLASHING_DWARF_CODE :
					if (slashDwarf->getStatus() == WAKE_UP)
						displayInfo.message.push_back("He's awake");
					else{
						slashDwarf->setStatus(WAKE_UP);
						player->decTimeLeft();
					}
				default :
					displayInfo.message.push_back("Unknown dwarf type");
				break;
				}
			break;

			//untuk mendapatkan status dari kurcaci tertentu
			case STATUS_COMMAND_CODE :
                if(commandToken.stringParam[0]==DWARF_STRING){
                    if (waterDwarf->getStatus()==WAKE_UP)
                        displayInfo.message.push_back("Watering Dwarf is awake.");
                    else
                        displayInfo.message.push_back("Watering Dwarf is sleeping.");
                    if (harvestDwarf->getStatus()==WAKE_UP)
                        displayInfo.message.push_back("Harvesting Dwarf is awake.");
                    else
                        displayInfo.message.push_back("Harvesting Dwarf is sleeping.");
                    if (slashDwarf->getStatus()==WAKE_UP)
                        displayInfo.message.push_back("Slashing Dwarf is awake.");
                    else
                        displayInfo.message.push_back("Slashing Dwarf is sleeping.");
                }
                else
                    displayInfo.message.push_back("Status what?");
			break;

			//untuk command yang tidak valid
			default :
				displayInfo.message.push_back("Command doesn't recognized");
			break;
			}
		break;

		//bila status area player tidak dikenali (untuk debug)
		default :
			displayInfo.message.push_back("Player get lost");
		break;
		}

	break;}
	}

	//gerakin dwarf dan kasi efek kejadian alam
	for(int i = player->getTimeLeft(); i < curTimeLeft; i++){
	    //dwarf
		int dummy;
		if (waterDwarf->getStatus() == WAKE_UP)
			dummy = waterDwarf->act(farm, WATERING_DWARF_CODE);
		if (slashDwarf->getStatus() == WAKE_UP)
			dummy = slashDwarf->act(farm, SLASHING_DWARF_CODE);
		if (harvestDwarf->getStatus() == WAKE_UP)
			dummy = harvestDwarf->act(farm, HARVESTING_DWARF_CODE);
		player->incBalance(dummy);
	}
	//kejadian alam
	if (player->getDay() % 5 ==0){
	    displayInfo.message.push_back("It's raining");
        weather.rainy(farm);
	}
	if (player->getDay() % 15 ==0){
        weather.storm(farm);
	}

	//mengembalikan display info
	displayInfo.farm = farm;
	displayInfo.player = *player;
	displayInfo.waterDwarf = *waterDwarf;
	displayInfo.harvestDwarf = *harvestDwarf;
	displayInfo.slashDwarf = *slashDwarf;
	return displayInfo;
}

//method bantuan, mengubah integer jadi string
string GameState::intToString(int number){
	stringstream ss;
	ss << number;
	return ss.str();
}

//mendapatkan display default pertama kali (bukan dari user)
DisplayInfo GameState::getFirstDisplay(){
	DisplayInfo displayInfo;
	//masuk ke gamestate
	displayInfo.state=GAME_STATE;
	//mengembalikan display info
	displayInfo.farm = farm;
	displayInfo.player = *player;
	return displayInfo;
}
